﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Opennav.MapGenerator.Map.Labels;
using System.IO;
using Opennav.MapGenerator.Map.Roads;
using Opennav.MapGenerator.Map.Locations;
using Opennav.MapGenerator.Map.MapTiles;
using Opennav.MapGenerator.Locator;
using Opennav.MapGenerator.Map.Search;

namespace Opennav.MapGenerator.Map
{
    public class MapFile
    {
        private LocationSubfile _locationFile;
        private MapTileSubfile _mapFile;
        private RoadSubfile _roadFile;
        private SearchSubfile _searchFile;
        private LabelSubfile _labelFile;

        public MapFile(IList<Zoom> zoomLevels)
        {
            _labelFile = new LabelSubfile();
            _mapFile = new MapTileSubfile();
            _locationFile = new LocationSubfile(_labelFile);
            _roadFile = new RoadSubfile();
            _searchFile = new SearchSubfile(_locationFile, _roadFile);
            _mapFile.ZoomLevels = zoomLevels.ToArray();
        }

        public void AddMapObject(MapObject obj)
        {
            _mapFile.Add(obj);
        }

        public City CreateCity(GeoPlace place)
        {
            return _locationFile.CreateCity(place);
        }

        public Label CreateLabel(string label)
        {
            return _labelFile.Create(label);
        }

        public void AddRoad(Road road) 
        {
            _roadFile.RoadNetwork.Add(road);
        }

        public void Optimize()
        {
            _roadFile.Optimize();
        }

        public void Preprocess()
        {
            _roadFile.Preprocess();
        }

        private void AddRoadLines()
        {
            foreach (Road road in _roadFile.RoadNetwork)
            {
                foreach (RoadSegment segment in road.Segments)
                {
                    foreach (MapLine roadLine in segment.MapLines)
                    {
                        AddMapObject(roadLine);
                    }
                }
            }
        }

        public void Generate(Stream stream)
        {
            AddRoadLines();
            int startOffset = (int)stream.Position;
            BinaryWriter writer = new BinaryWriter(stream);
            int locationFileOffset = 0;
            int mapFileOffset = 0;
            int roadFileOffset = 0;
            int searchFileOffset = 0;
            int labelFileOffset = 0;
            writer.Write(mapFileOffset);
            writer.Write(roadFileOffset);
            writer.Write(searchFileOffset);
            writer.Write(labelFileOffset);
            locationFileOffset = (int)stream.Position;
            _locationFile.Write1(stream);
            mapFileOffset = (int)stream.Position;
            _mapFile.Write(stream);
            roadFileOffset = (int)stream.Position;
            _roadFile.Write(stream);
            searchFileOffset = (int)stream.Position;
            _searchFile.Write(stream);
            labelFileOffset = (int)stream.Position;
            _labelFile.Write(stream);
            int endOffset = (int)stream.Position;
            writer.Seek(locationFileOffset, SeekOrigin.Begin);
            _locationFile.Write2(stream);
            writer.Seek(startOffset, SeekOrigin.Begin);
            writer.Write(mapFileOffset - startOffset);
            writer.Write(roadFileOffset - startOffset);
            writer.Write(searchFileOffset - startOffset);
            writer.Write(labelFileOffset - startOffset);
            stream.Seek(endOffset, SeekOrigin.Begin);
        }
    }
}
